﻿Imports System.Text

Public Class Matrix4
    Public Property M11() As Double
    Public Property M12() As Double
    Public Property M13() As Double
    Public Property M14() As Double
    Public Property M21() As Double
    Public Property M22() As Double
    Public Property M23() As Double
    Public Property M24() As Double
    Public Property M31() As Double
    Public Property M32() As Double
    Public Property M33() As Double
    Public Property M34() As Double
    Public Property M41() As Double
    Public Property M42() As Double
    Public Property M43() As Double
    Public Property M44() As Double

#Region "静态方法"

    Public Shared ReadOnly Property Identity As Matrix4
        Get
            Dim mt = Matrix4.Zero
            mt.M11 = 1
            mt.M22 = 1
            mt.M33 = 1
            mt.M44 = 1
            Return mt
        End Get
    End Property

    Public Shared ReadOnly Property Zero() As Matrix4
        Get
            Dim arr(0 To 3, 0 To 3) As Double
            Dim t = GetType(Matrix4)
            Dim pros = t.GetProperties().Where(Function(p) p.Name Like "M*" And p.CanWrite)
            For i = 1 To 4
                For j = 1 To 4
                    arr(i - 1, j - 1) = 0
                Next
            Next
            Return New Matrix4(arr)
        End Get
    End Property


#End Region


#Region "只读属性"

    ''' <summary>
    ''' 矩阵的行列式
    ''' </summary>
    ''' <returns></returns>
    Public ReadOnly Property Determinant() As Double
        Get
            Dim l1 = Me.M11 * Me.M22 * Me.M33 * Me.M44
            Dim l2 = Me.M12 * Me.M23 * Me.M34 * Me.M41
            Dim l3 = Me.M13 * Me.M24 * Me.M31 * Me.M42
            Dim l4 = Me.M14 * Me.M21 * Me.M32 * Me.M43

            Dim m1 = -Me.M14 * Me.M23 * Me.M32 * Me.M41
            Dim m2 = -Me.M13 * Me.M22 * Me.M31 * Me.M44
            Dim m3 = -Me.M12 * Me.M21 * Me.M34 * Me.M43
            Dim m4 = -Me.M11 * Me.M24 * Me.M33 * Me.M42
            Return l1 + l2 + l3 + l4 + m1 + m2 + m3 + m4
        End Get
    End Property

    Public ReadOnly Property IsIdentify() As Boolean
        Get
            Return If(M11 <> 1.0, False, True)
            Return If(M22 <> 1.0, False, True)
            Return If(M33 <> 1.0, False, True)
            Return If(M44 <> 1.0, False, True)
        End Get
    End Property
#End Region


    Sub New(arr(,) As Double)
        Debug.Assert(arr.GetLength(0) = 4 And arr.GetLength(1) = 4, "参数必须是4x4的数组")
        Dim t = GetType(Matrix4)
        Dim pros = t.GetProperties().Where(Function(p) p.Name Like "M*" And p.CanWrite)
        For i = 1 To 4
            For j = 1 To 4
                pros.First(Function(p) p.Name = "M" & i & j).SetValue(Me, arr(i - 1, j - 1), Nothing)
            Next
        Next
    End Sub
    Public Sub New()

    End Sub

#Region "方法"
    Public Function GetRow(rowNumber As Integer) As Double()
        Debug.Assert(rowNumber >= 1 And rowNumber <= 4, "rowNumber must is bigger than 0 and less than 5 ")
        Dim arr(0 To 3) As Double
        Dim t = GetType(Matrix4)
        Dim pros = t.GetProperties().Where(Function(p) p.Name Like "M" & rowNumber & "*" And p.CanWrite)
        For index = 1 To 4
            Dim pi = pros.First(Function(p) p.Name = "M" & rowNumber & index)
            arr(index - 1) = pi.GetValue(Me, Nothing)
        Next
        Return arr
    End Function

    Public Function GetColumn(columnNumber As Integer) As Double()
        Debug.Assert(columnNumber >= 1 And columnNumber <= 4, "columnNumber must is bigger than 0 and less than 5 ")
        Dim arr(0 To 3) As Double
        Dim t = GetType(Matrix4)
        Dim pros = t.GetProperties().Where(Function(p) p.Name Like "M*" & columnNumber And p.CanWrite)
        For index = 1 To 4
            arr(index - 1) = pros.First(Function(p) p.Name = "M" & index & columnNumber).GetValue(Me, Nothing)
        Next
        Return arr
    End Function

    Public Function Add(ByVal b As Matrix4) As Matrix4
        Return Me + b
    End Function
    Public Function subtraction(ByVal b As Matrix4) As Matrix4
        Return Me - b
    End Function

    Public Shared Function TranslationMatrix(ByVal dx As Double, ByVal dy As Double, ByVal dz As Double) As Matrix4
        Dim mt = Matrix4.Zero
        mt.M14 = dx
        mt.M24 = dy
        mt.M34 = dz
        mt.M11 = 1 : mt.M22 = 1 : mt.M33 = 1 : mt.M34 = 1
        Return mt
    End Function

    Public Shared Function ScaleMatrix(ByVal x As Double, ByVal y As Double, ByVal z As Double) As Matrix4
        Dim mt = Matrix4.Zero
        mt.M11 = x
        mt.M22 = y
        mt.M33 = z
        mt.M44 = 1
        Return mt
    End Function

    Public Shared Function RotateMatrix(angle As Double, rotateAlix As ClsVector3d) As Matrix4
        Dim v1 As ClsVector3d
        If Not rotateAlix.IsUnitVector3d Then
            v1 = rotateAlix.UnitVector
        Else
            v1 = rotateAlix
        End If

        Dim u = v1.X, v = v1.Y, w = v1.Z
        Dim rtn(0 To 3, 0 To 3) As Double

        rtn(0, 0) = u ^ 2 + (1 - u ^ 2) * Math.Cos(angle)
        rtn(0, 1) = u * v * (1 - Math.Cos(angle)) - w * Math.Sin(angle)
        rtn(0, 2) = u * w * (1 - Math.Cos(angle)) + v * Math.Sin(angle) : rtn(0, 3) = 0

        rtn(1, 0) = u * v * (1 - Math.Cos(angle)) + w * Math.Sin(angle)
        rtn(1, 1) = v ^ 2 + (1 - v ^ 2) * Math.Cos(angle)
        rtn(1, 2) = v * w * (1 - Math.Cos(angle)) - u * Math.Sin(angle) : rtn(1, 3) = 0

        rtn(2, 0) = u * w * (1 - Math.Cos(angle)) - v * Math.Sin(angle)
        rtn(2, 1) = v * w * (1 - Math.Cos(angle)) + u * Math.Sin(angle)
        rtn(2, 2) = w ^ 2 + (1 - w ^ 2) * Math.Cos(angle) : rtn(2, 3) = 0

        rtn(3, 0) = 0 : rtn(3, 1) = 0 : rtn(3, 2) = 0 : rtn(3, 3) = 1
        Return New Matrix4(rtn)
    End Function



    Public Function ToArray() As Double(,)
        Dim arr(0 To 3, 0 To 3) As Double
        Dim t = GetType(Matrix4)
        Dim pros = t.GetProperties().Where(Function(p) p.Name Like "M*" And p.CanWrite)
        For i = 1 To 4
            For j = 1 To 4
                arr(i - 1, j - 1) = CDbl(pros.First(Function(p) p.Name = "M" & i & j).GetValue(Me, Nothing))
            Next
        Next
        Return arr
    End Function

    Public Overrides Function ToString() As String
        Dim sb As New StringBuilder
        Dim t = GetType(Matrix4)
        Dim pros = t.GetProperties().Where(Function(p) p.Name Like "M*" And p.CanWrite)
        For i = 1 To 4
            For j = 1 To 4
                sb.Append(CDbl(pros.First(Function(p) p.Name = "M" & i & j).GetValue(Me, Nothing)) & If(j < 4, ",", ""))
            Next
            sb.Append(vbNewLine)
        Next

        Return sb.ToString()
    End Function
#End Region

#Region "操作符"
    Public Shared Operator +(a As Matrix4, b As Matrix4) As Matrix4
        Dim arr(0 To 3, 0 To 3) As Double
        Dim t = GetType(Matrix4)
        Dim pros = t.GetProperties().Where(Function(p) p.Name Like "M*" And p.CanWrite)
        For i = 1 To 4
            For j = 1 To 4
                arr(i - 1, j - 1) = CDbl(pros.First(Function(p) p.Name = "M" & i & j).GetValue(a, Nothing)) _
                                    + CDbl(pros.First(Function(p) p.Name = "M" & i & j).GetValue(b, Nothing))
            Next
        Next
        Return New Matrix4(arr)
    End Operator

    Public Shared Operator -(a As Matrix4, b As Matrix4) As Matrix4
        Dim arr(0 To 3, 0 To 3) As Double
        Dim t = GetType(Matrix4)
        Dim pros = t.GetProperties().Where(Function(p) p.Name Like "M*" And p.CanWrite)
        For i = 1 To 4
            For j = 1 To 4
                arr(i - 1, j - 1) = CDbl(pros.First(Function(p) p.Name = "M" & i & j).GetValue(a, Nothing)) _
                                    - CDbl(pros.First(Function(p) p.Name = "M" & i & j).GetValue(b, Nothing))
            Next
        Next
        Return New Matrix4(arr)
    End Operator

    Public Shared Operator *(a As Matrix4, k As Double) As Matrix4
        Dim arr(0 To 3, 0 To 3) As Double
        Dim t = GetType(Matrix4)
        Dim pros = t.GetProperties().Where(Function(p) p.Name Like "M*" And p.CanWrite)
        For i = 1 To 4
            For j = 1 To 4
                arr(i - 1, j - 1) = CDbl(pros.First(Function(p) p.Name = "M" & i & j).GetValue(a, Nothing)) * k
            Next
        Next
        Return New Matrix4(arr)
    End Operator

    Public Shared Operator *(a As Matrix4, b As Matrix4) As Matrix4
        Dim arr(0 To 3, 0 To 3) As Double
        For i = 1 To 4
            Dim aCurRow = a.GetRow(i)
            For j = 1 To 4
                Dim aCurCol = b.GetColumn(j)
                Dim curLocationVal As Double = 0#
                For k = 0 To 3
                    curLocationVal += aCurRow(k) * aCurCol(k)
                Next
                arr(i - 1, j - 1) = curLocationVal
            Next
        Next
        Return New Matrix4(arr)
    End Operator


    Public Shared Operator *(a As Matrix4, p As ClsPoint3d) As ClsPoint3d

        Dim rtn As New ClsPoint3d

        Dim arrRtn(0 To 3) As Double, arrIn(0 To 3) As Double

        arrIn(0) = p.X : arrIn(1) = p.Y : arrIn(2) = p.Z : arrIn(3) = 1.0

        Dim t = GetType(Matrix4)
        Dim pros = t.GetProperties().Where(Function(pro) pro.Name Like "M*" And pro.CanWrite)


        For i = 1 To 4
            Dim val As Double = 0.0#
            For j = 1 To 4
                Val += CDbl(pros.First(Function(pro) pro.Name = "M" & i & j).GetValue(a, Nothing)) * arrIn(j - 1)
            Next
            arrRtn(i - 1) = val
        Next
        rtn.X = arrRtn(0) : rtn.Y = arrRtn(1) : rtn.Z = arrRtn(2)
        Return rtn
    End Operator
#End Region



End Class
